<?php

class Workset_File_Info extends SplFileInfo implements ArrayAccess
{

	protected static $_basePaths = null;

	protected static $_keys = array(
		'id'		=> 'getId',
		'name' 		=> 'getFilename',
		'shortpath' => 'getRelatedPath',
		'size' 		=> 'getSize',
		'atime'   	=> 'getATime',
		'ctime' 	=> 'getCTime',
		'mtime' 	=> 'getMTime',
		'realpath' 	=> 'getRealPath',
		'path' 		=> 'getPath',
		'pathname' 	=> 'getPathname',
		'perms' 	=> 'getPerms',
		'owner' 	=> 'getOwner',
		'group' 	=> 'getGroup',
		'inode' 	=> 'getInode',
		'dir' 		=> 'isDir',
		'file' 		=> 'isFile',
		'link' 		=> 'isLink',
		'executable' 	=> 'isExecutable',
		'readable' 		=> 'isReadable',
		'writable' 		=> 'isWritable',
		'mimetype' 		=> 'getMimetype'
	);

    protected $_id = null;

	protected $_info = null;

    protected $_handler = null;

    protected $_handlerClass = null;

    static function addBasePath($path) {
        $path = rtrim($path, '\/');

        if (null === self::$_basePaths) {
            self::$_basePaths = array();
        }

		self::$_basePaths[] = str_replace(DIRECTORY_SEPARATOR, '/', $path);
    }


	/**
	 * Устанавливает единый базовый путь для файлов.
	 *
	 * @static
	 * @param  $path
	 * @return void
	 */
	static function setBasePath($path) {
        self::$_basePaths = null;
        self::addBasePath($path);
	}

    /**
     * Устанавливает базовын пути для файлов
     *
     * @static
     * @param  $paths
     * @return string
     */
    static function setBasePaths(array $paths) {
        self::$_basePaths = null;

        foreach ($paths as $path) {
            self::addBasePath($path);
        }
    }

	/**
	 * Возвращает базовый путь для файлов
	 *
	 * @static
	 * @return string
	 */
	static function getBasePaths() {
		return self::$_basePaths;
	}

    static function getFullPath($shortPath) {
        $basepaths = self::getBasePaths();

        if (!$basepaths) {
            throw new Zend_Exception('Undefined base path to file ' . $shortPath);
        }

        $shortPath = trim($shortPath, '\/');

        foreach ($basepaths as $basepath) {

            $filename = $basepath . DIRECTORY_SEPARATOR . $shortPath;

            if (file_exists($filename)) {
                return $filename;
            }
        }

        throw new Zend_Exception('Undefined full path for ' . $shortPath);
    }

    /**
     * @throws Zend_Exception
     * @param   $filename
     * @param   bool $realpath
     * @return void
     */
	public function __construct($filename, $realpath = true) {
        if (false == $realpath) {
            $filename = self::getFullPath($filename);
        }

		parent::__construct($filename);

        if (null !== $this->_handler) {
            $this->setHandler($this->_handler);
        }
	}

	/**
	 * Устанавливает объект-обработчик файла (например GD для изображений)
	 *
	 * @throws Zend_Exception
	 * @param  $handler
	 * @return void
	 */
    public function setHandler($handler) {
        if (is_string($handler) && class_exists($handler)) {
            $this->_handlerClass = $handler;

        } elseif ($handler instanceof Workset_File_Handler_Abstract) {
            $this->_handler = $handler;
            
        } else {
            throw new Zend_Exception('Unknown file handler');
        }
    }

	/**
	 * Возвращает обработчик файла
	 *
	 * @throws Zend_Exception
	 * @return Workset_File_Handler_Abstract
	 */
    public function getHandler() {
        if (null === $this->_handler) {
            if (is_string($this->_handlerClass)) {
               $this->_handler = new $this->_handlerClass($this);
            } else {
               throw new Zend_Exception('Unknown file handler');
            }
        }

        return $this->_handler;
    }

	/**
	 * Возвращает относительный путь к файлу, вычисляется относительно
	 * базового пути, устанавливаемого через self::setBasePath($path);
	 *
	 * Если базовый путь не задан, возвращается обсалютный путь к файлу,
	 * как и при вызове $this->getRealPath()
	 *
	 * @return string
	 */
	public function getRelatedPath() {
        if (null === self::$_basePaths) {
            $path = $this->getPathname();
            //$path = $this->getRealPath();
        } else {
            $path = $this->getPathname();
            //$path = $this->getRealPath();
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);

            foreach (self::$_basePaths as $basepath) {
                if (0 === strpos($path, $basepath)) {
                    $path = substr($path, strlen($basepath));
                    break;
                }
            }
        }

		return $path;
	}

	/**
	 * Возвращает строку mime-type файла.
	 *
	 * Для корректной работы должно быть включено расширение finfo.
	 *
	 * @return string
	 */
    public function getMimetype() {
        $mime = Workset_File_Info_Helper::getMimetype($this->getRealPath());

        if (null !== $mime) {
            return $mime;
        }

        return $mime;
    }

    /**
     * Возвращает уникальный ID файла
     *
     * @return string
     */
	public function getId() {
        if (null === $this->_id) {
            $this->_id = uniqid();
        }
		return $this->_id;
	}

	/**
	 * Возвращает массив с информацией о файле.
	 *
	 * Возвращаемые поля и соответствующие им вызовы методов:
	 *
	 * array(
	 *	'id'		=> 'getId',
	 *	'name' 		=> 'getFilename',
	 *	'shortpath' => 'getRelatedPath',
	 *	'size' 		=> 'getSize',
	 *	'atime'   	=> 'getATime',
	 *	'ctime' 	=> 'getCTime',
	 *	'mtime' 	=> 'getMTime',
	 *	'realpath' 	=> 'getRealPath',
	 *	'path' 		=> 'getPath',
	 *	'pathname' 	=> 'getPathname',
	 *	'perms' 	=> 'getPerms',
	 *	'owner' 	=> 'getOwner',
	 *	'group' 	=> 'getGroup',
	 *	'inode' 	=> 'getInode',
	 *	'dir' 		=> 'isDir',
	 *	'file' 		=> 'isFile',
	 *	'link' 		=> 'isLink',
	 *	'executable' 	=> 'isExecutable',
	 *	'readable' 		=> 'isReadable',
	 *	'writable' 		=> 'isWritable',
	 *	'mimetype' 		=> 'getMimetype'
	 *	)
	 *
	 * @param array $systeminfo - вернуть только указанные поля
	 * @return array
	 */
    public function toArray($systeminfo = array()) {
        $result = array();

		if (!$systeminfo) {
			$systeminfo = array_keys(self::$_keys);
		}
		
		foreach ($systeminfo as $val) {
			if (!isset($result[$val])) {
				$result[$val] = $this->{self::$_keys[$val]}();
			}
		}

        return $result;
    }

	/**
	 * ArrayAccess implementation
	 *
	 * @param  $key
	 * @param  $value
	 * @return void
	 */
	public function offsetSet($key, $value) {
        if (null === $this->_info) {
            $this->_info = $this->toArray();
        }
		$this->_info[$key] = $value;
	}

	/**
	 * ArrayAccess implementation
	 *
	 * @param  $key
	 * @return mixed
	 */
	public function offsetGet($key) {
		if (null === $this->_info) {
            $this->_info = $this->toArray();
        }
		return isset($this->_info[$key]) ? $this->_info[$key] : null;
	}

	/**
	 * ArrayAccess implementation
	 *
	 * @param  $key
	 * @return void
	 */
	public function offsetUnset($key) {
        if (null === $this->_info) {
            $this->_info = $this->toArray();
        }
		unset($this->_info[$key]);
	}

	/**
	 * ArrayAccess implementation
	 *
	 * @param  $key
	 * @return bool
	 */
	public function offsetExists($key) {
        if (null === $this->_info) {
            $this->_info = $this->toArray();
        }
		return isset($this->_info[$key]);
	}

}
